package com.gitee.wsl.time.chinese

import com.gitee.wsl.data.cache.Cache
import com.gitee.wsl.text.format.format
import com.gitee.wsl.time.chinese.bean.EightChar
import com.gitee.wsl.time.chinese.bean.Foto
import com.gitee.wsl.time.chinese.bean.Fu
import com.gitee.wsl.time.chinese.bean.JieQi
import com.gitee.wsl.time.chinese.bean.NineStar
import com.gitee.wsl.time.chinese.bean.ShuJiu
import com.gitee.wsl.time.chinese.bean.Tao
import com.gitee.wsl.time.chinese.util.LunarUtil
import com.gitee.wsl.time.chinese.util.LunarUtil.getDayJi
import com.gitee.wsl.time.chinese.util.LunarUtil.getDayJiShen
import com.gitee.wsl.time.chinese.util.LunarUtil.getDayXiongSha
import com.gitee.wsl.time.chinese.util.LunarUtil.getDayYi
import com.gitee.wsl.time.chinese.util.LunarUtil.getJiaZiIndex
import com.gitee.wsl.time.chinese.util.LunarUtil.getTimeJi
import com.gitee.wsl.time.chinese.util.LunarUtil.getTimeYi
import com.gitee.wsl.time.chinese.util.LunarUtil.getTimeZhiIndex
import com.gitee.wsl.time.chinese.util.LunarUtil.getXun
import com.gitee.wsl.time.chinese.util.LunarUtil.getXunKong
import com.gitee.wsl.time.chinese.util.SolarUtil
import com.gitee.wsl.time.now
import kotlinx.datetime.LocalDate
import kotlinx.datetime.LocalDateTime
import kotlin.jvm.JvmOverloads
import kotlin.math.abs


/**
 * 农历日期
 *
 * @author 6tail
 */
class Lunar {
    /**
     * 农历年
     */
    var year: Int = 0
        private set
    /**
     * 农历月，闰月为负，即闰2月=-2
     */
    var month: Int = 0
        private set
    /**
     * 农历日
     */
    var day: Int = 0
        private set

    /**
     * 对应阳历
     */
    val solar: Solar

    /**
     * 时对应的天干下标，0-9
     */
    var timeGanIndex: Int = 0
        private set

    /**
     * 时对应的地支下标，0-11
     */
    var timeZhiIndex: Int = 0
        private set

    /**
     * 日对应的天干下标，0-9
     */
    var dayGanIndex: Int = 0
        private set

    /**
     * 日对应的地支下标，0-11
     */
    var dayZhiIndex: Int = 0
        private set

    /**
     * 日对应的天干下标（八字流派1，晚子时日柱算明天），0-9
     */
    var dayGanIndexExact: Int = 0
        private set

    /**
     * 日对应的地支下标（八字流派1，晚子时日柱算明天），0-11
     */
    var dayZhiIndexExact: Int = 0
        private set

    /**
     * 日对应的天干下标（八字流派2，晚子时日柱算当天），0-9
     */
    var dayGanIndexExact2: Int = 0
        private set

    /**
     * 日对应的地支下标（八字流派2，晚子时日柱算当天），0-11
     */
    var dayZhiIndexExact2: Int = 0
        private set

    /**
     * 月对应的天干下标（以节交接当天起算），0-9
     */
    var monthGanIndex: Int = 0
        private set

    /**
     * 月对应的地支下标（以节交接当天起算），0-11
     */
    var monthZhiIndex: Int = 0
        private set

    /**
     * 月对应的天干下标（最精确的，供八字用，以节交接时刻起算），0-9
     */
    var monthGanIndexExact: Int = 0
        private set

    /**
     * 月对应的地支下标（最精确的，供八字用，以节交接时刻起算），0-11
     */
    var monthZhiIndexExact: Int = 0
        private set

    /**
     * 年对应的天干下标（国标，以正月初一为起点），0-9
     */
    var yearGanIndex: Int = 0
        private set

    /**
     * 年对应的地支下标（国标，以正月初一为起点），0-11
     */
    var yearZhiIndex: Int = 0
        private set

    /**
     * 年对应的天干下标（月干计算用，以立春为起点），0-9
     */
    var yearGanIndexByLiChun: Int = 0
        private set

    /**
     * 年对应的地支下标（月支计算用，以立春为起点），0-11
     */
    var yearZhiIndexByLiChun: Int = 0
        private set

    /**
     * 年对应的天干下标（最精确的，供八字用，以立春交接时刻为起点），0-9
     */
    var yearGanIndexExact: Int = 0
        private set

    /**
     * 年对应的地支下标（最精确的，供八字用，以立春交接时刻为起点），0-11
     */
    var yearZhiIndexExact: Int = 0
        private set

    /**
     * 获取星期，0代表周日，1代表周一
     *
     * @return 0123456
     */
    /**
     * 周下标，0-6
     */
    var week: Int = 0
        private set

    /**
     * 阳历小时
     */
    val hour: Int

    /**
     * 阳历分钟
     */
    val minute: Int

    /**
     * 阳历秒钟
     */
    val second: Int

    /**
     * 八字
     */
    var eightChar: EightChar? = null
        get() {
            if (null == field) {
                field = EightChar(this)
            }
            return field
        }
        private set
    /**
     * 获取节气表（节气名称:阳历），节气交接时刻精确到秒，以冬至开头，按先后顺序排列
     *
     * @return 节气表
     */
    /**
     * 24节气表（对应阳历的准确时刻）
     */
    val jieQiTable = LinkedHashMap<String, Solar>()

    /**
     * 通过农历年月日时初始化
     *
     * @param lunarYear  年（农历）
     * @param lunarMonth 月（农历），1到12，闰月为负，即闰2月=-2
     * @param lunarDay   日（农历），1到30
     * @param hour       小时（阳历）
     * @param minute     分钟（阳历）
     * @param second     秒钟（阳历）
     */
    /**
     * 通过农历年月日初始化
     *
     * @param lunarYear  年（农历）
     * @param lunarMonth 月（农历），1到12，闰月为负，即闰2月=-2
     * @param lunarDay   日（农历），1到30
     */
    @JvmOverloads
    constructor(lunarYear: Int, lunarMonth: Int, lunarDay: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) {
        var y = LunarYear.fromYear(lunarYear)
        val m = y.getMonth(lunarMonth)
        requireNotNull(m) { String.format("wrong lunar year %d month %d", lunarYear, lunarMonth) }
        require(lunarDay >= 1) { "lunar day must bigger than 0" }
        val days = m.dayCount
        require(lunarDay <= days) {
            String.format(
                "only %d days in lunar year %d month %d",
                days,
                lunarYear,
                lunarMonth
            )
        }
        this.year = lunarYear
        this.month = lunarMonth
        this.day = lunarDay
        this.hour = hour
        this.minute = minute
        this.second = second
        val noon = Solar.fromJulianDay(m.firstJulianDay + lunarDay - 1)
        this.solar = Solar.fromYmdHms(noon.year, noon.month, noon.day, hour, minute, second)
        if (noon.year != lunarYear) {
            y = LunarYear.fromYear(noon.year)
        }
        compute(y)
    }

    /**
     * 通过阳历初始化
     *
     * @param solar 阳历
     */
    constructor(solar: Solar) {
        val ly = LunarYear.fromYear(solar.year)
        for (m in ly.months) {
            val days = solar.subtract(Solar.fromJulianDay(m.firstJulianDay))
            if (days < m.dayCount) {
                year = m.year
                month = m.month
                day = days + 1
                break
            }
        }
        hour = solar.hour
        minute = solar.minute
        second = solar.second
        this.solar = solar
        compute(ly)
    }

    /**
     * 通过阳历日期初始化
     *
     * @param date 阳历日期
     */
    /**
     * 默认使用当前日期初始化
     */
    @JvmOverloads
    constructor(date: LocalDate = LocalDate.now()) : this(Solar.fromDate(date))

    /**
     * 计算节气表
     */
    private fun computeJieQi(lunarYear: LunarYear) {
        val julianDays: List<Double?> = lunarYear.jieQiJulianDays
        var i = 0
        val j = JIE_QI_IN_USE.size
        while (i < j) {
            jieQiTable.put(JIE_QI_IN_USE[i], Solar.fromJulianDay(julianDays.get(i)!!))
            i++
        }
    }

    /**
     * 计算干支纪年
     */
    private fun computeYear() {
        //以正月初一开始
        val offset = year - 4
        yearGanIndex = offset % 10
        yearZhiIndex = offset % 12

        if (yearGanIndex < 0) {
            yearGanIndex += 10
        }

        if (yearZhiIndex < 0) {
            yearZhiIndex += 12
        }

        //以立春作为新一年的开始的干支纪年
        var g = yearGanIndex
        var z = yearZhiIndex

        //精确的干支纪年，以立春交接时刻为准
        var gExact = yearGanIndex
        var zExact = yearZhiIndex

        val solarYear = solar.year
        val solarYmd = solar.toYmd()
        val solarYmdHms = solar.toYmdHms()

        //获取立春的阳历时刻
        var liChun: Solar = jieQiTable.get("立春")!!
        if (liChun.year != solarYear) {
            liChun = jieQiTable.get("LI_CHUN")!!
        }
        val liChunYmd = liChun.toYmd()
        val liChunYmdHms = liChun.toYmdHms()

        //阳历和阴历年份相同代表正月初一及以后
        if (year == solarYear) {
            //立春日期判断
            if (solarYmd.compareTo(liChunYmd) < 0) {
                g--
                z--
            }
            //立春交接时刻判断
            if (solarYmdHms.compareTo(liChunYmdHms) < 0) {
                gExact--
                zExact--
            }
        } else if (year < solarYear) {
            if (solarYmd.compareTo(liChunYmd) >= 0) {
                g++
                z++
            }
            if (solarYmdHms.compareTo(liChunYmdHms) >= 0) {
                gExact++
                zExact++
            }
        }

        yearGanIndexByLiChun = (if (g < 0) g + 10 else g) % 10
        yearZhiIndexByLiChun = (if (z < 0) z + 12 else z) % 12

        yearGanIndexExact = (if (gExact < 0) gExact + 10 else gExact) % 10
        yearZhiIndexExact = (if (zExact < 0) zExact + 12 else zExact) % 12
    }

    /**
     * 计算干支纪月
     */
    private fun computeMonth() {
        var start: Solar? = null
        var end: Solar
        val ymd = solar.toYmd()
        val time = solar.toYmdHms()
        val size = JIE_QI_IN_USE.size

        //序号：大雪以前-3，大雪到小寒之间-2，小寒到立春之间-1，立春之后0
        var index = -3
        run {
            var i = 0
            while (i < size) {
                end = jieQiTable.get(JIE_QI_IN_USE[i])!!
                val symd = if (null == start) ymd else start.toYmd()
                if (ymd.compareTo(symd) >= 0 && ymd.compareTo(end.toYmd()) < 0) {
                    break
                }
                start = end
                index++
                i += 2
            }
        }

        //干偏移值（以立春当天起算）
        var offset = (((yearGanIndexByLiChun + (if (index < 0) 1 else 0)) % 5 + 1) * 2) % 10
        monthGanIndex = ((if (index < 0) index + 10 else index) + offset) % 10
        monthZhiIndex = ((if (index < 0) index + 12 else index) + LunarUtil.BASE_MONTH_ZHI_INDEX) % 12

        start = null
        index = -3
        var i = 0
        while (i < size) {
            end = jieQiTable.get(JIE_QI_IN_USE[i])!!
            val stime = if (null == start) time else start.toYmdHms()
            if (time.compareTo(stime) >= 0 && time.compareTo(end.toYmdHms()) < 0) {
                break
            }
            start = end
            index++
            i += 2
        }

        //干偏移值（以立春交接时刻起算）
        offset = (((yearGanIndexExact + (if (index < 0) 1 else 0)) % 5 + 1) * 2) % 10
        monthGanIndexExact = ((if (index < 0) index + 10 else index) + offset) % 10
        monthZhiIndexExact = ((if (index < 0) index + 12 else index) + LunarUtil.BASE_MONTH_ZHI_INDEX) % 12
    }

    /**
     * 计算干支纪日
     */
    private fun computeDay() {
        val noon = Solar.fromYmdHms(solar.year, solar.month, solar.day, 12, 0, 0)
        val offset = noon.julianDay.toInt() - 11
        dayGanIndex = offset % 10
        dayZhiIndex = offset % 12

        var dayGanExact = dayGanIndex
        var dayZhiExact = dayZhiIndex

        // 八字流派2，晚子时（夜子/子夜）日柱算当天
        dayGanIndexExact2 = dayGanExact
        dayZhiIndexExact2 = dayZhiExact

        // 八字流派1，晚子时（夜子/子夜）日柱算明天
        val hm = (if (hour < 10) "0" else "") + hour + ":" + (if (minute < 10) "0" else "") + minute
        if (hm.compareTo("23:00") >= 0 && hm.compareTo("23:59") <= 0) {
            dayGanExact++
            if (dayGanExact >= 10) {
                dayGanExact -= 10
            }
            dayZhiExact++
            if (dayZhiExact >= 12) {
                dayZhiExact -= 12
            }
        }

        dayGanIndexExact = dayGanExact
        dayZhiIndexExact = dayZhiExact
    }

    /**
     * 计算干支纪时
     */
    private fun computeTime() {
        val hm = (if (hour < 10) "0" else "") + hour + ":" + (if (minute < 10) "0" else "") + minute
        timeZhiIndex = getTimeZhiIndex(hm)
        timeGanIndex = (dayGanIndexExact % 5 * 2 + timeZhiIndex) % 10
    }

    /**
     * 计算星期
     */
    private fun computeWeek() {
        this.week = solar.week
    }

    private fun compute(lunarYear: LunarYear) {
        computeJieQi(lunarYear)
        computeYear()
        computeMonth()
        computeDay()
        computeTime()
        computeWeek()
    }

    @get:Deprecated("使用getYearGan")
    val gan: String
        /**
         * 获取年份的天干（以正月初一作为新年的开始）
         *
         * @return 天干，如辛
         */
        get() = this.yearGan

    val yearGan: String
        /**
         * 获取年份的天干（以正月初一作为新年的开始）
         *
         * @return 天干，如辛
         */
        get() = LunarUtil.GAN[yearGanIndex + 1]

    val yearGanByLiChun: String
        /**
         * 获取年份的天干（以立春当天作为新年的开始）
         *
         * @return 天干，如辛
         */
        get() = LunarUtil.GAN[yearGanIndexByLiChun + 1]

    val yearGanExact: String
        /**
         * 获取最精确的年份天干（以立春交接的时刻作为新年的开始）
         *
         * @return 天干，如辛
         */
        get() = LunarUtil.GAN[yearGanIndexExact + 1]

    @get:Deprecated("使用getYearZhi")
    val zhi: String
        /**
         * 获取年份的地支（以正月初一作为新年的开始）
         *
         * @return 地支，如亥
         */
        get() = this.yearZhi

    val yearZhi: String
        /**
         * 获取年份的地支（以正月初一作为新年的开始）
         *
         * @return 地支，如亥
         */
        get() = LunarUtil.ZHI[yearZhiIndex + 1]

    val yearZhiByLiChun: String
        /**
         * 获取年份的地支（以立春当天作为新年的开始）
         *
         * @return 地支，如亥
         */
        get() = LunarUtil.ZHI[yearZhiIndexByLiChun + 1]

    val yearZhiExact: String
        /**
         * 获取最精确的年份地支（以立春交接的时刻作为新年的开始）
         *
         * @return 地支，如亥
         */
        get() = LunarUtil.ZHI[yearZhiIndexExact + 1]

    val yearInGanZhi: String
        /**
         * 获取干支纪年（年柱）（以正月初一作为新年的开始）
         *
         * @return 年份的干支（年柱），如辛亥
         */
        get() = this.yearGan + this.yearZhi

    val yearInGanZhiByLiChun: String
        /**
         * 获取干支纪年（年柱）（以立春当天作为新年的开始）
         *
         * @return 年份的干支（年柱），如辛亥
         */
        get() = this.yearGanByLiChun + this.yearZhiByLiChun

    val yearInGanZhiExact: String
        /**
         * 获取干支纪年（年柱）（以立春交接的时刻作为新年的开始）
         *
         * @return 年份的干支（年柱），如辛亥
         */
        get() = this.yearGanExact + this.yearZhiExact

    val monthInGanZhi: String
        /**
         * 获取干支纪月（月柱）（以节交接当天起算）
         *
         * 月天干口诀：甲己丙寅首，乙庚戊寅头。丙辛从庚寅，丁壬壬寅求，戊癸甲寅居，周而复始流。
         *
         * 月地支：正月起寅
         *
         * @return 干支纪月（月柱），如己卯
         */
        get() = this.monthGan + this.monthZhi

    val monthInGanZhiExact: String
        /**
         * 获取精确的干支纪月（月柱）（以节交接时刻起算）
         *
         * 月天干口诀：甲己丙寅首，乙庚戊寅头。丙辛从庚寅，丁壬壬寅求，戊癸甲寅居，周而复始流。
         *
         * 月地支：正月起寅
         *
         * @return 干支纪月（月柱），如己卯
         */
        get() = this.monthGanExact + this.monthZhiExact

    val monthGan: String
        /**
         * 获取月天干（以节交接当天起算）
         *
         * @return 月天干，如己
         */
        get() = LunarUtil.GAN[monthGanIndex + 1]

    val monthGanExact: String
        /**
         * 获取精确的月天干（以节交接时刻起算）
         *
         * @return 月天干，如己
         */
        get() = LunarUtil.GAN[monthGanIndexExact + 1]

    val monthZhi: String
        /**
         * 获取月地支（以节交接当天起算）
         *
         * @return 月地支，如卯
         */
        get() = LunarUtil.ZHI[monthZhiIndex + 1]

    val monthZhiExact: String
        /**
         * 获取精确的月地支（以节交接时刻起算）
         *
         * @return 月地支，如卯
         */
        get() = LunarUtil.ZHI[monthZhiIndexExact + 1]

    val dayInGanZhi: String
        /**
         * 获取干支纪日（日柱）
         *
         * @return 干支纪日（日柱），如己卯
         */
        get() = this.dayGan + this.dayZhi

    val dayInGanZhiExact: String
        /**
         * 获取干支纪日（日柱，晚子时日柱算明天）
         *
         * @return 干支纪日（日柱），如己卯
         */
        get() = this.dayGanExact + this.dayZhiExact

    val dayInGanZhiExact2: String
        /**
         * 获取干支纪日（日柱，晚子时日柱算当天）
         *
         * @return 干支纪日（日柱），如己卯
         */
        get() = this.dayGanExact2 + this.dayZhiExact2

    val dayGan: String
        /**
         * 获取日天干
         *
         * @return 日天干，如甲
         */
        get() = LunarUtil.GAN[dayGanIndex + 1]

    val dayGanExact: String
        /**
         * 获取日天干（晚子时日柱算明天）
         *
         * @return 日天干，如甲
         */
        get() = LunarUtil.GAN[dayGanIndexExact + 1]

    val dayGanExact2: String
        /**
         * 获取日天干（晚子时日柱算当天）
         *
         * @return 日天干，如甲
         */
        get() = LunarUtil.GAN[dayGanIndexExact2 + 1]

    val dayZhi: String
        /**
         * 获取日地支
         *
         * @return 日地支，如卯
         */
        get() = LunarUtil.ZHI[dayZhiIndex + 1]

    val dayZhiExact: String
        /**
         * 获取日地支（晚子时日柱算明天）
         *
         * @return 日地支，如卯
         */
        get() = LunarUtil.ZHI[dayZhiIndexExact + 1]

    val dayZhiExact2: String
        /**
         * 获取日地支（晚子时日柱算当天）
         *
         * @return 日地支，如卯
         */
        get() = LunarUtil.ZHI[dayZhiIndexExact2 + 1]


    val yearShengXiao: String
        /**
         * 获取年生肖（以正月初一起算）
         *
         * @return 年生肖，如虎
         */
        get() = LunarUtil.SHENGXIAO[yearZhiIndex + 1]

    val yearShengXiaoByLiChun: String
        /**
         * 获取年生肖（以立春当天起算）
         *
         * @return 年生肖，如虎
         */
        get() = LunarUtil.SHENGXIAO[yearZhiIndexByLiChun + 1]

    val yearShengXiaoExact: String
        /**
         * 获取精确的年生肖（以立春交接时刻起算）
         *
         * @return 年生肖，如虎
         */
        get() = LunarUtil.SHENGXIAO[yearZhiIndexExact + 1]

    val monthShengXiao: String
        /**
         * 获取月生肖
         *
         * @return 月生肖，如虎
         */
        get() = LunarUtil.SHENGXIAO[monthZhiIndex + 1]

    val dayShengXiao: String
        /**
         * 获取日生肖
         *
         * @return 日生肖，如虎
         */
        get() = LunarUtil.SHENGXIAO[dayZhiIndex + 1]

    val timeShengXiao: String
        /**
         * 获取时辰生肖
         *
         * @return 时辰生肖，如虎
         */
        get() = LunarUtil.SHENGXIAO[timeZhiIndex + 1]

    val yearInChinese: String
        /**
         * 获取中文的年
         *
         * @return 中文年，如二零零一
         */
        get() {
            val y = year.toString() + ""
            val s = StringBuilder()
            var i = 0
            val j = y.length
            while (i < j) {
                s.append(LunarUtil.NUMBER[y.get(i).code - '0'.code])
                i++
            }
            return s.toString()
        }

    val monthInChinese: String
        /**
         * 获取中文的月
         *
         * @return 中文月，如正
         */
        get() = (if (month < 0) "闰" else "") + LunarUtil.MONTH[abs(month)]

    val dayInChinese: String
        /**
         * 获取中文日
         *
         * @return 中文日，如初一
         */
        get() = LunarUtil.DAY[day]

    val timeZhi: String
        /**
         * 获取时辰（地支）
         *
         * @return 时辰（地支）
         */
        get() = LunarUtil.ZHI[timeZhiIndex + 1]

    val timeGan: String
        /**
         * 获取时辰（天干）
         *
         * @return 时辰（天干）
         */
        get() = LunarUtil.GAN[timeGanIndex + 1]

    val timeInGanZhi: String
        /**
         * 获取时辰干支（时柱），支持早子时和晚子时
         *
         * @return 时辰干支（时柱）
         */
        get() = this.timeGan + this.timeZhi

    val season: String
        /**
         * 获取季节
         *
         * @return 农历季节
         */
        get() = LunarUtil.SEASON[abs(month)]

    protected fun convertJieQi(name: String): String {
        var jq = name
        if ("DONG_ZHI" == jq) {
            jq = "冬至"
        } else if ("DA_HAN" == jq) {
            jq = "大寒"
        } else if ("XIAO_HAN" == jq) {
            jq = "小寒"
        } else if ("LI_CHUN" == jq) {
            jq = "立春"
        } else if ("DA_XUE" == jq) {
            jq = "大雪"
        } else if ("YU_SHUI" == jq) {
            jq = "雨水"
        } else if ("JING_ZHE" == jq) {
            jq = "惊蛰"
        }
        return jq
    }

    val jie: String
        /**
         * 获取节令
         *
         * @return 节令
         */
        get() {
            var i = 0
            val j = JIE_QI_IN_USE.size
            while (i < j) {
                val key = JIE_QI_IN_USE[i]
                val d: Solar = jieQiTable.get(key)!!
                if (d.year == solar.year && d.month == solar.month && d.day == solar.day) {
                    return convertJieQi(key)
                }
                i += 2
            }
            return ""
        }

    val qi: String
        /**
         * 获取气令
         *
         * @return 气令
         */
        get() {
            var i = 1
            val j = JIE_QI_IN_USE.size
            while (i < j) {
                val key = JIE_QI_IN_USE[i]
                val d: Solar = jieQiTable.get(key)!!
                if (d.year == solar.year && d.month == solar.month && d.day == solar.day) {
                    return convertJieQi(key)
                }
                i += 2
            }
            return ""
        }

    val weekInChinese: String
        /**
         * 获取星期的中文
         *
         * @return 日一二三四五六
         */
        get() = SolarUtil.WEEK[this.week]

    val xiu: String
        /**
         * 获取宿
         *
         * @return 宿
         */
        get() = LunarUtil.XIU.get(this.dayZhi + this.week)!!

    val xiuLuck: String
        /**
         * 获取宿吉凶
         *
         * @return 吉/凶
         */
        get() = LunarUtil.XIU_LUCK.get(this.xiu)!!

    val xiuSong: String
        /**
         * 获取宿歌诀
         *
         * @return 宿歌诀
         */
        get() = LunarUtil.XIU_SONG.get(this.xiu)!!

    val zheng: String
        /**
         * 获取政
         *
         * @return 政
         */
        get() = LunarUtil.ZHENG.get(this.xiu)!!

    val animal: String
        /**
         * 获取动物
         *
         * @return 动物
         */
        get() = LunarUtil.ANIMAL.get(this.xiu)!!

    val gong: String
        /**
         * 获取宫
         *
         * @return 宫
         */
        get() = LunarUtil.GONG.get(this.xiu)!!

    val shou: String
        /**
         * 获取兽
         *
         * @return 兽
         */
        get() = LunarUtil.SHOU.get(this.gong)!!

    val festivals: List<String>
        /**
         * 获取节日，有可能一天会有多个节日
         *
         * @return 节日列表，如春节
         */
        get() {
            val l = ArrayList<String>()
            val f = LunarUtil.FESTIVAL.get(month.toString() + "-" + day)
            if (null != f) {
                l.add(f)
            }
            if (abs(month) == 12 && day >= 29 && year != next(1).year) {
                l.add("除夕")
            }
            return l
        }

    val otherFestivals: List<String>
        /**
         * 获取非正式的节日，有可能一天会有多个节日
         *
         * @return 非正式的节日列表，如中元节
         */
        get() {
            val l = ArrayList<String>()
            val fs: List<String>? = LunarUtil.OTHER_FESTIVAL.get(month.toString() + "-" + day)
            if (null != fs) {
                l.addAll(fs)
            }
            val solarYmd = solar.toYmd()
            if (solarYmd == jieQiTable.get("清明")!!.next(-1).toYmd()) {
                l.add("寒食节")
            }
            var jq: Solar = jieQiTable.get("立春")!!
            var offset = 4 - jq.lunar.dayGanIndex
            if (offset < 0) {
                offset += 10
            }
            if (solarYmd == jq.next(offset + 40).toYmd()) {
                l.add("春社")
            }

            jq = jieQiTable.get("立秋")!!
            offset = 4 - jq.lunar.dayGanIndex
            if (offset < 0) {
                offset += 10
            }
            if (solarYmd == jq.next(offset + 40).toYmd()) {
                l.add("秋社")
            }
            return l
        }

    val pengZuGan: String
        /**
         * 获取彭祖百忌天干
         *
         * @return 彭祖百忌天干
         */
        get() = LunarUtil.PENGZU_GAN[dayGanIndex + 1]

    val pengZuZhi: String
        /**
         * 获取彭祖百忌地支
         *
         * @return 彭祖百忌地支
         */
        get() = LunarUtil.PENGZU_ZHI[dayZhiIndex + 1]


    val dayPositionXi: String
        /**
         * 获取日喜神方位
         *
         * @return 方位，如艮
         */
        get() = LunarUtil.POSITION_XI[dayGanIndex + 1]

    val dayPositionXiDesc: String
        /**
         * 获取日喜神方位描述
         *
         * @return 方位描述，如东北
         */
        get() = LunarUtil.POSITION_DESC.get(this.dayPositionXi)!!

    val dayPositionYangGui: String
        /**
         * 获取日阳贵神方位
         *
         * @return 方位，如艮
         */
        get() = LunarUtil.POSITION_YANG_GUI[dayGanIndex + 1]

    val dayPositionYangGuiDesc: String
        /**
         * 获取日阳贵神方位描述
         *
         * @return 方位描述，如东北
         */
        get() = LunarUtil.POSITION_DESC.get(this.dayPositionYangGui)!!

    val dayPositionYinGui: String
        /**
         * 获取日阴贵神方位
         *
         * @return 阴贵神方位，如艮
         */
        get() = LunarUtil.POSITION_YIN_GUI[dayGanIndex + 1]

    val dayPositionYinGuiDesc: String
        /**
         * 获取日阴贵神方位描述
         *
         * @return 方位描述，如东北
         */
        get() = LunarUtil.POSITION_DESC.get(this.dayPositionYinGui)!!

    val dayPositionFu: String
        /**
         * 获取日福神方位（默认流派：2）
         *
         * @return 福神方位，如艮
         */
        get() = getDayPositionFu(2)

    /**
     * 获取日福神方位
     *
     * @param sect 流派，1或2
     * @return 方位，如艮
     */
    fun getDayPositionFu(sect: Int): String {
        return (if (1 == sect) LunarUtil.POSITION_FU else LunarUtil.POSITION_FU_2)[dayGanIndex + 1]
    }

    val dayPositionFuDesc: String
        /**
         * 获取日福神方位描述（默认流派：2）
         *
         * @return 方位描述，如东北
         */
        get() = getDayPositionFuDesc(2)

    /**
     * 获取日福神方位描述
     *
     * @param sect 流派，1或2
     * @return 方位描述，如东北
     */
    fun getDayPositionFuDesc(sect: Int): String {
        return LunarUtil.POSITION_DESC.get(getDayPositionFu(sect))!!
    }

    val dayPositionCai: String
        /**
         * 获取日财神方位
         *
         * @return 方位，如艮
         */
        get() = LunarUtil.POSITION_CAI[dayGanIndex + 1]

    val dayPositionCaiDesc: String
        /**
         * 获取日财神方位描述
         *
         * @return 方位描述，如东北
         */
        get() = LunarUtil.POSITION_DESC.get(this.dayPositionCai)!!

    val yearPositionTaiSui: String
        /**
         * 获取年太岁方位（默认流派2新年以立春零点起算）
         *
         * @return 方位，如艮
         */
        get() = getYearPositionTaiSui(2)

    /**
     * 获取年太岁方位
     *
     * @param sect 流派：2为新年以立春零点起算；1为新年以正月初一起算；3为新年以立春节气交接的时刻起算
     * @return 方位，如艮
     */
    fun getYearPositionTaiSui(sect: Int): String {
        val yearZhiIndex: Int
        when (sect) {
            1 -> yearZhiIndex = this.yearZhiIndex
            3 -> yearZhiIndex = this.yearZhiIndexExact
            else -> yearZhiIndex = this.yearZhiIndexByLiChun
        }
        return LunarUtil.POSITION_TAI_SUI_YEAR[yearZhiIndex]
    }

    val yearPositionTaiSuiDesc: String
        /**
         * 获取年太岁方位描述（默认流派2新年以立春零点起算）
         *
         * @return 方位描述，如东北
         */
        get() = getYearPositionTaiSuiDesc(2)

    /**
     * 获取年太岁方位描述
     *
     * @param sect 流派：2为新年以立春零点起算；1为新年以正月初一起算；3为新年以立春节气交接的时刻起算
     * @return 方位描述，如东北
     */
    fun getYearPositionTaiSuiDesc(sect: Int): String {
        return LunarUtil.POSITION_DESC.get(getYearPositionTaiSui(sect))!!
    }

    protected fun getMonthPositionTaiSui(monthZhiIndex: Int, monthGanIndex: Int): String {
        val p: String
        var m = monthZhiIndex - LunarUtil.BASE_MONTH_ZHI_INDEX
        if (m < 0) {
            m += 12
        }
        m = m % 4
        when (m) {
            0 -> p = "艮"
            2 -> p = "坤"
            3 -> p = "巽"
            else -> p = LunarUtil.POSITION_GAN[monthGanIndex]
        }
        return p
    }

    val monthPositionTaiSui: String
        /**
         * 获取月太岁方位（默认流派2新的一月以节交接当天零点起算）
         *
         * @return 太岁方位，如艮
         */
        get() = getMonthPositionTaiSui(2)

    /**
     * 获取月太岁方位
     *
     * @param sect 流派：2为新的一月以节交接当天零点起算；3为新的一月以节交接准确时刻起算
     * @return 太岁方位，如艮
     */
    fun getMonthPositionTaiSui(sect: Int): String {
        val monthZhiIndex: Int
        val monthGanIndex: Int
        if (sect == 3) {
            monthZhiIndex = this.monthZhiIndexExact
            monthGanIndex = this.monthGanIndexExact
        } else {
            monthZhiIndex = this.monthZhiIndex
            monthGanIndex = this.monthGanIndex
        }
        return getMonthPositionTaiSui(monthZhiIndex, monthGanIndex)
    }

    val monthPositionTaiSuiDesc: String
        /**
         * 获取月太岁方位描述（默认流派2新的一月以节交接当天零点起算）
         *
         * @return 太岁方位描述，如东北
         */
        get() = getMonthPositionTaiSuiDesc(2)

    /**
     * 获取月太岁方位描述
     *
     * @param sect 流派：2为新的一月以节交接当天零点起算；3为新的一月以节交接准确时刻起算
     * @return 太岁方位描述，如东北
     */
    fun getMonthPositionTaiSuiDesc(sect: Int): String {
        return LunarUtil.POSITION_DESC.get(getMonthPositionTaiSui(sect))!!
    }

    protected fun getDayPositionTaiSui(dayInGanZhi: String, yearZhiIndex: Int): String {
        val p: String
        if ("甲子,乙丑,丙寅,丁卯,戊辰,己巳".contains(dayInGanZhi)) {
            p = "震"
        } else if ("丙子,丁丑,戊寅,己卯,庚辰,辛巳".contains(dayInGanZhi)) {
            p = "离"
        } else if ("戊子,己丑,庚寅,辛卯,壬辰,癸巳".contains(dayInGanZhi)) {
            p = "中"
        } else if ("庚子,辛丑,壬寅,癸卯,甲辰,乙巳".contains(dayInGanZhi)) {
            p = "兑"
        } else if ("壬子,癸丑,甲寅,乙卯,丙辰,丁巳".contains(dayInGanZhi)) {
            p = "坎"
        } else {
            p = LunarUtil.POSITION_TAI_SUI_YEAR[yearZhiIndex]
        }
        return p
    }

    val dayPositionTaiSui: String
        /**
         * 获取日太岁方位（默认流派2新年以立春零点起算）
         *
         * @return 太岁方位，如艮
         */
        get() = getDayPositionTaiSui(2)

    /**
     * 获取日太岁方位
     *
     * @param sect 流派：2新年以立春零点起算；1新年以正月初一起算；3新年以立春节气交接的时刻起算
     * @return 太岁方位，如艮
     */
    fun getDayPositionTaiSui(sect: Int): String {
        val dayInGanZhi: String
        val yearZhiIndex: Int
        when (sect) {
            1 -> {
                dayInGanZhi = this.dayInGanZhi
                yearZhiIndex = this.yearZhiIndex
            }

            3 -> {
                dayInGanZhi = this.dayInGanZhi
                yearZhiIndex = this.yearZhiIndexExact
            }

            else -> {
                dayInGanZhi = this.dayInGanZhiExact2
                yearZhiIndex = this.yearZhiIndexByLiChun
            }
        }
        return getDayPositionTaiSui(dayInGanZhi, yearZhiIndex)
    }

    val dayPositionTaiSuiDesc: String
        /**
         * 获取日太岁方位描述（默认流派2新年以立春零点起算）
         *
         * @return 太岁方位描述，如东北
         */
        get() = getDayPositionTaiSuiDesc(2)

    /**
     * 获取日太岁方位描述
     *
     * @param sect 流派：2新年以立春零点起算；1新年以正月初一起算；3新年以立春节气交接的时刻起算
     * @return 太岁方位描述，如东北
     */
    fun getDayPositionTaiSuiDesc(sect: Int): String {
        return LunarUtil.POSITION_DESC.get(getDayPositionTaiSui(sect))!!
    }

    val timePositionXi: String
        /**
         * 获取时辰喜神方位
         *
         * @return 喜神方位，如艮
         */
        get() = LunarUtil.POSITION_XI[timeGanIndex + 1]

    val timePositionXiDesc: String
        /**
         * 获取时辰喜神方位描述
         *
         * @return 喜神方位描述，如东北
         */
        get() = LunarUtil.POSITION_DESC.get(this.timePositionXi)!!

    val timePositionYangGui: String
        /**
         * 获取时辰阳贵神方位
         *
         * @return 阳贵神方位，如艮
         */
        get() = LunarUtil.POSITION_YANG_GUI[timeGanIndex + 1]

    val timePositionYangGuiDesc: String
        /**
         * 获取时辰阳贵神方位描述
         *
         * @return 阳贵神方位描述，如东北
         */
        get() = LunarUtil.POSITION_DESC.get(this.timePositionYangGui)!!

    val timePositionYinGui: String
        /**
         * 获取时辰阴贵神方位
         *
         * @return 阴贵神方位，如艮
         */
        get() = LunarUtil.POSITION_YIN_GUI[timeGanIndex + 1]

    val timePositionYinGuiDesc: String
        /**
         * 获取时辰阴贵神方位描述
         *
         * @return 阴贵神方位描述，如东北
         */
        get() = LunarUtil.POSITION_DESC.get(this.timePositionYinGui)!!

    val timePositionFu: String
        /**
         * 获取时辰福神方位，默认流派2
         *
         * @return 福神方位，如艮
         */
        get() = getTimePositionFu(2)

    /**
     * 获取时辰福神方位
     *
     * @param sect 流派，1或2
     * @return 福神方位，如艮
     */
    fun getTimePositionFu(sect: Int): String {
        return (if (1 == sect) LunarUtil.POSITION_FU else LunarUtil.POSITION_FU_2)[timeGanIndex + 1]
    }

    val timePositionFuDesc: String
        /**
         * 获取时辰福神方位描述，默认流派2
         *
         * @return 福神方位描述，如东北
         */
        get() = getTimePositionFuDesc(2)

    /**
     * 获取时辰福神方位描述
     *
     * @param sect 流派，1或2
     * @return 福神方位描述，如东北
     */
    fun getTimePositionFuDesc(sect: Int): String {
        return LunarUtil.POSITION_DESC.get(getTimePositionFu(sect))!!
    }

    val timePositionCai: String
        /**
         * 获取时辰财神方位
         *
         * @return 财神方位，如艮
         */
        get() = LunarUtil.POSITION_CAI[timeGanIndex + 1]

    val timePositionCaiDesc: String
        /**
         * 获取时辰财神方位描述
         *
         * @return 财神方位描述，如东北
         */
        get() = LunarUtil.POSITION_DESC.get(this.timePositionCai)!!

    val yearNaYin: String
        /**
         * 获取年纳音
         *
         * @return 年纳音，如剑锋金
         */
        get() = LunarUtil.NAYIN.get(this.yearInGanZhi)!!

    val monthNaYin: String
        /**
         * 获取月纳音
         *
         * @return 月纳音，如剑锋金
         */
        get() = LunarUtil.NAYIN.get(this.monthInGanZhi)!!

    val dayNaYin: String
        /**
         * 获取日纳音
         *
         * @return 日纳音，如剑锋金
         */
        get() = LunarUtil.NAYIN.get(this.dayInGanZhi)!!

    val timeNaYin: String
        /**
         * 获取时辰纳音
         *
         * @return 时辰纳音，如剑锋金
         */
        get() = LunarUtil.NAYIN.get(this.timeInGanZhi)!!

    val zhiXing: String
        /**
         * 获取十二执星：建、除、满、平、定、执、破、危、成、收、开、闭。当月支与日支相同即为建，依次类推
         *
         * @return 执星
         */
        get() {
            var offset = dayZhiIndex - monthZhiIndex
            if (offset < 0) {
                offset += 12
            }
            return LunarUtil.ZHI_XING[offset + 1]
        }

    val dayTianShen: String
        /**
         * 获取值日天神
         *
         * @return 值日天神
         */
        get() = LunarUtil.TIAN_SHEN[(dayZhiIndex + LunarUtil.ZHI_TIAN_SHEN_OFFSET.get(this.monthZhi)!!) % 12 + 1]

    val timeTianShen: String
        /**
         * 获取值时天神
         *
         * @return 值时天神
         */
        get() = LunarUtil.TIAN_SHEN[(timeZhiIndex + LunarUtil.ZHI_TIAN_SHEN_OFFSET.get(this.dayZhiExact)!!) % 12 + 1]

    val dayTianShenType: String
        /**
         * 获取值日天神类型：黄道/黑道
         *
         * @return 值日天神类型：黄道/黑道
         */
        get() = LunarUtil.TIAN_SHEN_TYPE.get(this.dayTianShen)!!

    val timeTianShenType: String
        /**
         * 获取值时天神类型：黄道/黑道
         *
         * @return 值时天神类型：黄道/黑道
         */
        get() = LunarUtil.TIAN_SHEN_TYPE.get(this.timeTianShen)!!

    val dayTianShenLuck: String
        /**
         * 获取值日天神吉凶
         *
         * @return 吉/凶
         */
        get() = LunarUtil.TIAN_SHEN_TYPE_LUCK.get(this.dayTianShenType)!!

    val timeTianShenLuck: String
        /**
         * 获取值时天神吉凶
         *
         * @return 吉/凶
         */
        get() = LunarUtil.TIAN_SHEN_TYPE_LUCK.get(this.timeTianShenType)!!

    val dayPositionTai: String
        /**
         * 获取逐日胎神方位
         *
         * @return 逐日胎神方位
         */
        get() = LunarUtil.POSITION_TAI_DAY[getJiaZiIndex(this.dayInGanZhi)]

    val monthPositionTai: String
        /**
         * 获取逐月胎神方位，闰月无
         *
         * @return 逐月胎神方位
         */
        get() {
            if (month < 0) {
                return ""
            }
            return LunarUtil.POSITION_TAI_MONTH[month - 1]
        }

    val dayYi: List<String?>?
        /**
         * 使用默认流派1（以节交接当天起算月）获取每日宜，如果没有，返回["无"]
         *
         * @return 宜
         */
        get() = getDayYi(1)

    /**
     * 获取每日宜，如果没有，返回["无"]
     *
     * @param sect 流派，1以节交接当天起算月，2以节交接时刻起算月
     * @return 宜
     */
    fun getDayYi(sect: Int): List<String> {
        return getDayYi(if (2 == sect) this.monthInGanZhiExact else this.monthInGanZhi, this.dayInGanZhi)
    }

    val dayJi: List<String>
        /**
         * 使用默认流派1（以节交接当天起算月）获取每日忌，如果没有，返回["无"]
         *
         * @return 忌
         */
        get() = getDayJi(1)

    /**
     * 获取每日忌，如果没有，返回["无"]
     *
     * @param sect 流派，1以节交接当天起算月，2以节交接时刻起算月
     * @return 忌
     */
    fun getDayJi(sect: Int): List<String> {
        return getDayJi(if (2 == sect) this.monthInGanZhiExact else this.monthInGanZhi, this.dayInGanZhi)
    }

    val dayJiShen: List<String>
        /**
         * 获取日吉神（宜趋），如果没有，返回["无"]
         *
         * @return 日吉神
         */
        get() = getDayJiShen(this.monthZhiIndex, this.dayInGanZhi)

    val dayXiongSha: List<String>
        /**
         * 获取日凶煞（宜忌），如果没有，返回["无"]
         *
         * @return 日凶煞
         */
        get() = getDayXiongSha(this.monthZhiIndex, this.dayInGanZhi)

    val dayChong: String
        /**
         * 获取日冲
         *
         * @return 日冲，如申
         */
        get() = LunarUtil.CHONG[dayZhiIndex]

    val daySha: String
        /**
         * 获取日煞
         *
         * @return 日煞，如北
         */
        get() = LunarUtil.SHA.get(this.dayZhi)!!

    val dayChongDesc: String
        /**
         * 获取日冲描述
         *
         * @return 日冲描述，如(壬申)猴
         */
        get() = "(" + this.dayChongGan + this.dayChong + ")" + this.dayChongShengXiao

    val dayChongShengXiao: String
        /**
         * 获取日冲生肖
         *
         * @return 日冲生肖，如猴
         */
        get() {
            val chong = this.dayChong
            var i = 0
            val j = LunarUtil.ZHI.size
            while (i < j) {
                if (LunarUtil.ZHI[i].equals(chong)) {
                    return LunarUtil.SHENGXIAO[i]
                }
                i++
            }
            return ""
        }

    val dayChongGan: String
        /**
         * 获取无情之克的日冲天干
         *
         * @return 无情之克的日冲天干，如甲
         */
        get() = LunarUtil.CHONG_GAN[dayGanIndex]

    val dayChongGanTie: String
        /**
         * 获取有情之克的日冲天干
         *
         * @return 有情之克的日冲天干，如甲
         */
        get() = LunarUtil.CHONG_GAN_TIE[dayGanIndex]

    val timeChong: String
        /**
         * 获取时冲
         *
         * @return 时冲，如申
         */
        get() = LunarUtil.CHONG[timeZhiIndex]

    val timeSha: String
        /**
         * 获取时煞
         *
         * @return 时煞，如北
         */
        get() = LunarUtil.SHA.get(this.timeZhi)!!

    val timeChongShengXiao: String
        /**
         * 获取时冲生肖
         *
         * @return 时冲生肖，如猴
         */
        get() {
            val chong = this.timeChong
            var i = 0
            val j = LunarUtil.ZHI.size
            while (i < j) {
                if (LunarUtil.ZHI[i].equals(chong)) {
                    return LunarUtil.SHENGXIAO[i]
                }
                i++
            }
            return ""
        }

    val timeChongDesc: String
        /**
         * 获取时冲描述
         *
         * @return 时冲描述，如(壬申)猴
         */
        get() = "(" + this.timeChongGan + this.timeChong + ")" + this.timeChongShengXiao

    val timeChongGan: String
        /**
         * 获取无情之克的时冲天干
         *
         * @return 无情之克的时冲天干，如甲
         */
        get() = LunarUtil.CHONG_GAN[timeGanIndex]

    val timeChongGanTie: String
        /**
         * 获取有情之克的时冲天干
         *
         * @return 有情之克的时冲天干，如甲
         */
        get() = LunarUtil.CHONG_GAN_TIE[timeGanIndex]

    val timeYi: List<String>
        /**
         * 获取时辰宜，如果没有，返回["无"]
         *
         * @return 宜
         */
        get() = getTimeYi(this.dayInGanZhiExact, this.timeInGanZhi)

    val timeJi: List<String>
        /**
         * 获取时辰忌，如果没有，返回["无"]
         *
         * @return 忌
         */
        get() = getTimeJi(this.dayInGanZhiExact, this.timeInGanZhi)

    val yueXiang: String
        /**
         * 获取月相
         *
         * @return 月相
         */
        get() = LunarUtil.YUE_XIANG[day]

    protected fun getYearNineStar(yearInGanZhi: String): NineStar {
        val indexExact = getJiaZiIndex(yearInGanZhi) + 1
        val index = getJiaZiIndex(this.yearInGanZhi) + 1
        var yearOffset = indexExact - index
        if (yearOffset > 1) {
            yearOffset -= 60
        } else if (yearOffset < -1) {
            yearOffset += 60
        }
        val yuan = ((this.year + yearOffset + 2696) / 60) % 3
        var offset = (62 + yuan * 3 - indexExact) % 9
        if (0 == offset) {
            offset = 9
        }
        return NineStar.fromIndex(offset - 1)
    }

    val yearNineStar: NineStar
        /**
         * 获取值年九星（默认流派2新年以立春零点起算。流年紫白星起例歌诀：年上吉星论甲子，逐年星逆中宫起；上中下作三元汇，一上四中七下兑。）
         *
         * @return 九星
         */
        get() = getYearNineStar(2)

    /**
     * 获取值年九星（流年紫白星起例歌诀：年上吉星论甲子，逐年星逆中宫起；上中下作三元汇，一上四中七下兑。）
     *
     * @param sect 流派：2为新年以立春零点起算；1为新年以正月初一起算；3为新年以立春节气交接的时刻起算
     * @return 九星
     */
    fun getYearNineStar(sect: Int): NineStar {
        val yearInGanZhi: String
        when (sect) {
            1 -> yearInGanZhi = this.yearInGanZhi
            3 -> yearInGanZhi = this.yearInGanZhiExact
            else -> yearInGanZhi = this.yearInGanZhiByLiChun
        }
        return getYearNineStar(yearInGanZhi)
    }

    protected fun getMonthNineStar(yearZhiIndex: Int, monthZhiIndex: Int): NineStar {
        val index = yearZhiIndex % 3
        var n = 27 - index * 3
        if (monthZhiIndex < LunarUtil.BASE_MONTH_ZHI_INDEX) {
            n -= 3
        }
        val offset = (n - monthZhiIndex) % 9
        return NineStar.fromIndex(offset)
    }

    val monthNineStar: NineStar
        /**
         * 获取值月九星（流派2新的一月以节交接当天零点起算。月紫白星歌诀：子午卯酉八白起，寅申巳亥二黑求，辰戌丑未五黄中。）
         *
         * @return 九星
         */
        get() = getMonthNineStar(2)

    /**
     * 获取值月九星（月紫白星歌诀：子午卯酉八白起，寅申巳亥二黑求，辰戌丑未五黄中。）
     *
     * @param sect 流派：2为新的一月以节交接当天零点起算；3为新的一月以节交接准确时刻起算
     * @return 九星
     */
    fun getMonthNineStar(sect: Int): NineStar {
        val yearZhiIndex: Int
        val monthZhiIndex: Int
        when (sect) {
            1 -> {
                yearZhiIndex = this.yearZhiIndex
                monthZhiIndex = this.monthZhiIndex
            }

            3 -> {
                yearZhiIndex = this.yearZhiIndexExact
                monthZhiIndex = this.monthZhiIndexExact
            }

            else -> {
                yearZhiIndex = this.yearZhiIndexByLiChun
                monthZhiIndex = this.monthZhiIndex
            }
        }
        return getMonthNineStar(yearZhiIndex, monthZhiIndex)
    }

    val dayNineStar: NineStar
        /**
         * 获取值日九星（日家紫白星歌诀：日家白法不难求，二十四气六宫周；冬至雨水及谷雨，阳顺一七四中游；夏至处暑霜降后，九三六星逆行求。）
         *
         * @return 九星
         */
        get() {
            val solarYmd = solar.toYmd()
            val dongZhi: Solar = jieQiTable.get("冬至")!!
            val dongZhi2: Solar = jieQiTable.get("DONG_ZHI")!!
            val xiaZhi: Solar = jieQiTable.get("夏至")!!
            val dongZhiIndex = getJiaZiIndex(dongZhi.lunar.dayInGanZhi)
            val dongZhiIndex2 = getJiaZiIndex(dongZhi2.lunar.dayInGanZhi)
            val xiaZhiIndex = getJiaZiIndex(xiaZhi.lunar.dayInGanZhi)
            val solarShunBai: Solar?
            val solarShunBai2: Solar?
            val solarNiZi: Solar?
            if (dongZhiIndex > 29) {
                solarShunBai = dongZhi.next(60 - dongZhiIndex)
            } else {
                solarShunBai = dongZhi.next(-dongZhiIndex)
            }
            val solarShunBaiYmd = solarShunBai.toYmd()
            if (dongZhiIndex2 > 29) {
                solarShunBai2 = dongZhi2.next(60 - dongZhiIndex2)
            } else {
                solarShunBai2 = dongZhi2.next(-dongZhiIndex2)
            }
            val solarShunBaiYmd2 = solarShunBai2.toYmd()
            if (xiaZhiIndex > 29) {
                solarNiZi = xiaZhi.next(60 - xiaZhiIndex)
            } else {
                solarNiZi = xiaZhi.next(-xiaZhiIndex)
            }
            val solarNiZiYmd = solarNiZi.toYmd()
            var offset = 0
            if (solarYmd.compareTo(solarShunBaiYmd) >= 0 && solarYmd.compareTo(solarNiZiYmd) < 0) {
                offset = solar.subtract(solarShunBai) % 9
            } else if (solarYmd.compareTo(solarNiZiYmd) >= 0 && solarYmd.compareTo(solarShunBaiYmd2) < 0) {
                offset = 8 - (solar.subtract(solarNiZi) % 9)
            } else if (solarYmd.compareTo(solarShunBaiYmd2) >= 0) {
                offset = solar.subtract(solarShunBai2) % 9
            } else if (solarYmd.compareTo(solarShunBaiYmd) < 0) {
                offset = (8 + solarShunBai.subtract(solar)) % 9
            }
            return NineStar.fromIndex(offset)
        }

    val timeNineStar: NineStar
        /**
         * 获取值时九星（时家紫白星歌诀：三元时白最为佳，冬至阳生顺莫差，孟日七宫仲一白，季日四绿发萌芽，每把时辰起甲子，本时星耀照光华，时星移入中宫去，顺飞八方逐细查。夏至阴生逆回首，孟归三碧季加六，仲在九宫时起甲，依然掌中逆轮跨。）
         *
         * @return 九星
         */
        get() {
            val solarYmd = solar.toYmd()
            var asc = false
            if ((solarYmd.compareTo(
                    jieQiTable.get("冬至")!!.toYmd()
                ) >= 0 && solarYmd.compareTo(jieQiTable.get("夏至")!!.toYmd()) < 0)
            ) {
                asc = true
            } else if (solarYmd.compareTo(jieQiTable.get("DONG_ZHI")!!.toYmd()) >= 0) {
                asc = true
            }
            var start = if (asc) 6 else 2
            val dayZhi = this.dayZhi
            if ("子午卯酉".contains(dayZhi)) {
                start = if (asc) 0 else 8
            } else if ("辰戌丑未".contains(dayZhi)) {
                start = if (asc) 3 else 5
            }
            val index = if (asc) start + timeZhiIndex else start + 9 - timeZhiIndex
            return NineStar(index % 9)
        }

    val nextJie: JieQi?
        /**
         * 获取下一节令（顺推的第一个节令）
         *
         * @return 节气
         */
        get() = getNextJie(false)

    /**
     * 获取下一节令（顺推的第一个节令）
     *
     * @param wholeDay 是否按天计
     * @return 节气
     */
    fun getNextJie(wholeDay: Boolean): JieQi? {
        val l = JIE_QI_IN_USE.size / 2
//        val conditions = kotlin.arrayOfNulls<String>(l)
//        for (i in 0..<l) {
//            conditions[i] = JIE_QI_IN_USE[i * 2]
//        }
        val conditions = Array(l){i->
            JIE_QI_IN_USE[i * 2]
        }
        return getNearJieQi(true, conditions, wholeDay)
    }

    val prevJie: JieQi?
        /**
         * 获取上一节令（逆推的第一个节令）
         *
         * @return 节气
         */
        get() = getPrevJie(false)

    /**
     * 获取上一节令（逆推的第一个节令）
     *
     * @param wholeDay 是否按天计
     * @return 节气
     */
    fun getPrevJie(wholeDay: Boolean): JieQi? {
        val l = JIE_QI_IN_USE.size / 2
//        val conditions = kotlin.arrayOfNulls<String>(l)
//        for (i in 0..<l) {
//            conditions[i] = JIE_QI_IN_USE[i * 2]
//        }
        val conditions = Array(l){i->
            JIE_QI_IN_USE[i * 2 ]
        }
        return getNearJieQi(false, conditions, wholeDay)
    }

    val nextQi: JieQi?
        /**
         * 获取下一气令（顺推的第一个气令）
         *
         * @return 节气
         */
        get() = getNextQi(false)

    /**
     * 获取下一气令（顺推的第一个气令）
     *
     * @param wholeDay 是否按天计
     * @return 节气
     */
    fun getNextQi(wholeDay: Boolean): JieQi? {
        val l = JIE_QI_IN_USE.size / 2
//        val conditions = kotlin.arrayOfNulls<String>(l)
//        for (i in 0..<l) {
//            conditions[i] = JIE_QI_IN_USE[i * 2 + 1]
//        }
        val conditions = Array(l){i->
            JIE_QI_IN_USE[i * 2 + 1]
        }
        return getNearJieQi(true, conditions, wholeDay)
    }

    val prevQi: JieQi?
        /**
         * 获取上一气令（逆推的第一个气令）
         *
         * @return 节气
         */
        get() = getPrevQi(false)

    /**
     * 获取上一气令（逆推的第一个气令）
     *
     * @param wholeDay 是否按天计
     * @return 节气
     */
    fun getPrevQi(wholeDay: Boolean): JieQi? {
        val l = JIE_QI_IN_USE.size / 2
//        val conditions = kotlin.arrayOfNulls<String>(l)
//        for (i in 0..<l) {
//            conditions[i] = JIE_QI_IN_USE[i * 2 + 1]
//        }
        val conditions = Array(l){i->
            JIE_QI_IN_USE[i * 2 + 1]
        }
        return getNearJieQi(false, conditions, wholeDay)
    }

    val nextJieQi: JieQi?
        /**
         * 获取下一节气（顺推的第一个节气）
         *
         * @return 节气
         */
        get() = getNextJieQi(false)

    /**
     * 获取下一节气（顺推的第一个节气）
     *
     * @param wholeDay 是否按天计
     * @return 节气
     */
    fun getNextJieQi(wholeDay: Boolean): JieQi? {
        return getNearJieQi(true, null, wholeDay)
    }

    val prevJieQi: JieQi?
        /**
         * 获取上一节气（逆推的第一个节气）
         *
         * @return 节气
         */
        get() = getPrevJieQi(false)

    /**
     * 获取上一节气（逆推的第一个节气）
     *
     * @param wholeDay 是否按天计
     * @return 节气
     */
    fun getPrevJieQi(wholeDay: Boolean): JieQi? {
        return getNearJieQi(false, null, wholeDay)
    }

    /**
     * 获取最近的节气，如果未找到匹配的，返回null
     *
     * @param forward    是否顺推，true为顺推，false为逆推
     * @param conditions 过滤条件，如果设置过滤条件，仅返回匹配该名称的
     * @param wholeDay   是否按天计
     * @return 节气
     */
    protected fun getNearJieQi(forward: Boolean, conditions: Array<String>?, wholeDay: Boolean): JieQi? {
        var name: String? = null
        var near: Solar? = null
        val filters = HashSet<String>()
        if (null != conditions) {
            filters.addAll(conditions)
        }
        val filter = !filters.isEmpty()
        val today = if (wholeDay) solar.toYmd() else solar.toYmdHms()
        for (entry in jieQiTable.entries) {
            val jq = convertJieQi(entry.key)
            if (filter) {
                if (!filters.contains(jq)) {
                    continue
                }
            }
            val solar: Solar = entry.value
            val day = if (wholeDay) solar.toYmd() else solar.toYmdHms()
            if (forward) {
                if (day.compareTo(today) <= 0) {
                    continue
                }
                if (null == near) {
                    name = jq
                    near = solar
                } else {
                    val nearDay = if (wholeDay) near.toYmd() else near.toYmdHms()
                    if (day.compareTo(nearDay) < 0) {
                        name = jq
                        near = solar
                    }
                }
            } else {
                if (day.compareTo(today) > 0) {
                    continue
                }
                if (null == near) {
                    name = jq
                    near = solar
                } else {
                    val nearDay = if (wholeDay) near.toYmd() else near.toYmdHms()
                    if (day.compareTo(nearDay) > 0) {
                        name = jq
                        near = solar
                    }
                }
            }
        }
        if (null == near) {
            return null
        }
        return JieQi(name, near)
    }

    /**
     * 获取节气名称，如果无节气，返回空字符串
     *
     * @return 节气名称
     */
    fun getJieQi(): String {
        for (jq in jieQiTable.entries) {
            val d: Solar = jq.value
            if (d.year == solar.year && d.month == solar.month && d.day == solar.day) {
                return convertJieQi(jq.key)
            }
        }
        return ""
    }

    val currentJieQi: JieQi?
        /**
         * 获取当天节气对象，如果无节气，返回null
         *
         * @return 节气对象
         */
        get() {
            for (jq in jieQiTable.entries) {
                val d: Solar = jq.value
                if (d.year == solar.year && d.month == solar.month && d.day == solar.day) {
                    return JieQi(convertJieQi(jq.key), d)
                }
            }
            return null
        }

    val currentJie: JieQi?
        /**
         * 获取当天节令对象，如果无节令，返回null
         *
         * @return 节气对象
         */
        get() {
            var i = 0
            val j = JIE_QI_IN_USE.size
            while (i < j) {
                val key = JIE_QI_IN_USE[i]
                val d: Solar = jieQiTable.get(key)!!
                if (d.year == solar.year && d.month == solar.month && d.day == solar.day) {
                    return JieQi(convertJieQi(key), d)
                }
                i += 2
            }
            return null
        }

    val currentQi: JieQi?
        /**
         * 获取当天气令对象，如果无气令，返回null
         *
         * @return 节气对象
         */
        get() {
            var i = 1
            val j = JIE_QI_IN_USE.size
            while (i < j) {
                val key = JIE_QI_IN_USE[i]
                val d: Solar = jieQiTable.get(key)!!
                if (d.year == solar.year && d.month == solar.month && d.day == solar.day) {
                    return JieQi(convertJieQi(key), d)
                }
                i += 2
            }
            return null
        }

    fun toFullString(): String {
        val s = StringBuilder()
        s.append(this)
        s.append(" ")
        s.append(this.yearInGanZhi)
        s.append("(")
        s.append(this.yearShengXiao)
        s.append(")年 ")
        s.append(this.monthInGanZhi)
        s.append("(")
        s.append(this.monthShengXiao)
        s.append(")月 ")
        s.append(this.dayInGanZhi)
        s.append("(")
        s.append(this.dayShengXiao)
        s.append(")日 ")
        s.append(this.timeZhi)
        s.append("(")
        s.append(this.timeShengXiao)
        s.append(")时 纳音[")
        s.append(this.yearNaYin)
        s.append(" ")
        s.append(this.monthNaYin)
        s.append(" ")
        s.append(this.dayNaYin)
        s.append(" ")
        s.append(this.timeNaYin)
        s.append("] 星期")
        s.append(this.weekInChinese)
        for (f in this.festivals) {
            s.append(" (")
            s.append(f)
            s.append(")")
        }
        for (f in this.otherFestivals) {
            s.append(" (")
            s.append(f)
            s.append(")")
        }
        val jq = getJieQi()
        if (jq.length > 0) {
            s.append(" [")
            s.append(jq)
            s.append("]")
        }
        s.append(" ")
        s.append(this.gong)
        s.append("方")
        s.append(this.shou)
        s.append(" 星宿[")
        s.append(this.xiu)
        s.append(this.zheng)
        s.append(this.animal)
        s.append("](")
        s.append(this.xiuLuck)
        s.append(") 彭祖百忌[")
        s.append(this.pengZuGan)
        s.append(" ")
        s.append(this.pengZuZhi)
        s.append("] 喜神方位[")
        s.append(this.dayPositionXi)
        s.append("](")
        s.append(this.dayPositionXiDesc)
        s.append(") 阳贵神方位[")
        s.append(this.dayPositionYangGui)
        s.append("](")
        s.append(this.dayPositionYangGuiDesc)
        s.append(") 阴贵神方位[")
        s.append(this.dayPositionYinGui)
        s.append("](")
        s.append(this.dayPositionYinGuiDesc)
        s.append(") 福神方位[")
        s.append(this.dayPositionFu)
        s.append("](")
        s.append(this.dayPositionFuDesc)
        s.append(") 财神方位[")
        s.append(this.dayPositionCai)
        s.append("](")
        s.append(this.dayPositionCaiDesc)
        s.append(") 冲[")
        s.append(this.dayChongDesc)
        s.append("] 煞[")
        s.append(this.daySha)
        s.append("]")
        return s.toString()
    }

    override fun toString(): String {
        return this.yearInChinese + "年" + this.monthInChinese + "月" + this.dayInChinese
    }

    /**
     * 获取往后推几天的农历日期，如果要往前推，则天数用负数
     *
     * @param days 天数
     * @return 农历日期
     */
    fun next(days: Int): Lunar {
        return solar.next(days).lunar
    }

    val yearXun: String
        /**
         * 获取年所在旬（以正月初一作为新年的开始）
         *
         * @return 旬
         */
        get() = getXun(this.yearInGanZhi)

    val yearXunByLiChun: String
        /**
         * 获取年所在旬（以立春当天作为新年的开始）
         *
         * @return 旬
         */
        get() = getXun(this.yearInGanZhiByLiChun)

    val yearXunExact: String
        /**
         * 获取年所在旬（以立春交接时刻作为新年的开始）
         *
         * @return 旬
         */
        get() = getXun(this.yearInGanZhiExact)

    val yearXunKong: String
        /**
         * 获取值年空亡（以正月初一作为新年的开始）
         *
         * @return 空亡(旬空)
         */
        get() = getXunKong(this.yearInGanZhi)

    val yearXunKongByLiChun: String
        /**
         * 获取值年空亡（以立春当天作为新年的开始）
         *
         * @return 空亡(旬空)
         */
        get() = getXunKong(this.yearInGanZhiByLiChun)

    val yearXunKongExact: String
        /**
         * 获取值年空亡（以立春交接时刻作为新年的开始）
         *
         * @return 空亡(旬空)
         */
        get() = getXunKong(this.yearInGanZhiExact)

    val monthXun: String
        /**
         * 获取月所在旬（以节交接当天起算）
         *
         * @return 旬
         */
        get() = getXun(this.monthInGanZhi)

    val monthXunExact: String
        /**
         * 获取月所在旬（以节交接时刻起算）
         *
         * @return 旬
         */
        get() = getXun(this.monthInGanZhiExact)

    val monthXunKong: String
        /**
         * 获取值月空亡（以节交接当天起算）
         *
         * @return 空亡(旬空)
         */
        get() = getXunKong(this.monthInGanZhi)

    val monthXunKongExact: String
        /**
         * 获取值月空亡（以节交接时刻起算）
         *
         * @return 空亡(旬空)
         */
        get() = getXunKong(this.monthInGanZhiExact)

    val dayXun: String
        /**
         * 获取日所在旬（以节交接当天起算）
         *
         * @return 旬
         */
        get() = getXun(this.dayInGanZhi)

    val dayXunExact: String
        /**
         * 获取日所在旬（晚子时日柱算明天）
         *
         * @return 旬
         */
        get() = getXun(this.dayInGanZhiExact)

    val dayXunExact2: String
        /**
         * 获取日所在旬（晚子时日柱算当天）
         *
         * @return 旬
         */
        get() = getXun(this.dayInGanZhiExact2)

    val dayXunKong: String
        /**
         * 获取值日空亡
         *
         * @return 空亡(旬空)
         */
        get() = getXunKong(this.dayInGanZhi)

    val dayXunKongExact: String
        /**
         * 获取值日空亡（晚子时日柱算明天）
         *
         * @return 空亡(旬空)
         */
        get() = getXunKong(this.dayInGanZhiExact)

    val dayXunKongExact2: String
        /**
         * 获取值日空亡（晚子时日柱算当天）
         *
         * @return 空亡(旬空)
         */
        get() = getXunKong(this.dayInGanZhiExact2)

    val timeXun: String
        /**
         * 获取时辰所在旬
         *
         * @return 旬
         */
        get() = getXun(this.timeInGanZhi)

    val timeXunKong: String
        /**
         * 获取值时空亡
         *
         * @return 空亡(旬空)
         */
        get() = getXunKong(this.timeInGanZhi)

    val shuJiu: ShuJiu?
        /**
         * 获取数九
         *
         * @return 数九，如果不是数九天，返回null
         */
        get() {
            val current = Solar.fromYmd(solar.year, solar.month, solar.day)
            var start: Solar = jieQiTable.get("DONG_ZHI")!!
            start = Solar.fromYmd(start.year, start.month, start.day)

            if (current.isBefore(start)) {
                start = jieQiTable.get("冬至")!!
                start = Solar.fromYmd(start.year, start.month, start.day)
            }

            val end = Solar.fromYmd(start.year, start.month, start.day).next(81)

            if (current.isBefore(start) || !current.isBefore(end)) {
                return null
            }

            val days = current.subtract(start)
            return ShuJiu(LunarUtil.NUMBER[days / 9 + 1] + "九", days % 9 + 1)
        }

    val fu: Fu?
        /**
         * 获取三伏
         *
         * @return 三伏，如果不是伏天，返回null
         */
        get() {
            val current = Solar.fromYmd(solar.year, solar.month, solar.day)
            val xiaZhi: Solar = jieQiTable.get("夏至")!!
            val liQiu: Solar = jieQiTable.get("立秋")!!
            var start = Solar.fromYmd(xiaZhi.year, xiaZhi.month, xiaZhi.day)
            // 第1个庚日
            var add = 6 - xiaZhi.lunar.dayGanIndex
            if (add < 0) {
                add += 10
            }
            // 第3个庚日，即初伏第1天
            add += 20
            start = start.next(add)

            // 初伏以前
            if (current.isBefore(start)) {
                return null
            }

            var days = current.subtract(start)
            if (days < 10) {
                return Fu("初伏", days + 1)
            }

            // 第4个庚日，中伏第1天
            start = start.next(10)
            days = current.subtract(start)
            if (days < 10) {
                return Fu("中伏", days + 1)
            }

            // 第5个庚日，中伏第11天或末伏第1天
            start = start.next(10)
            days = current.subtract(start)
            val liQiuSolar = Solar.fromYmd(liQiu.year, liQiu.month, liQiu.day)
            // 末伏
            if (liQiuSolar.isAfter(start)) {
                // 中伏
                if (days < 10) {
                    return Fu("中伏", days + 11)
                }
                // 末伏第1天
                start = start.next(10)
                days = current.subtract(start)
            }
            if (days < 10) {
                return Fu("末伏", days + 1)
            }
            return null
        }

    val liuYao: String
        /**
         * 获取六曜
         *
         * @return 六曜
         */
        get() = LunarUtil.LIU_YAO[(abs(month) - 1 + day - 1) % 6]

    val wuHou: String?
        /**
         * 获取物候
         *
         * @return 物候
         */
        get() {
            val jieQi = getPrevJieQi(true)?:return null
            var offset = 0
            var i = 0
            val j = JIE_QI.size
            while (i < j) {
                if (jieQi.getName().equals(JIE_QI[i])) {
                    offset = i
                    break
                }
                i++
            }
            var index = solar.subtract(jieQi.solar) / 5
            if (index > 2) {
                index = 2
            }
            return LunarUtil.WU_HOU[(offset * 3 + index) % LunarUtil.WU_HOU.size]
        }

    val hou: String?
        /**
         * 获取候
         *
         * @return 候
         */
        get() {
            val jieQi = getPrevJieQi(true)?:return null
            val max = LunarUtil.HOU.size - 1
            var offset = solar.subtract(jieQi.solar) / 5
            if (offset > max) {
                offset = max
            }
            return String.format("%s %s", jieQi.getName(), LunarUtil.HOU[offset])
        }

    val dayLu: String
        /**
         * 获取日禄
         *
         * @return 日禄
         */
        get() {
            val gan = LunarUtil.LU.get(this.dayGan)
            val zhi = LunarUtil.LU.get(this.dayZhi)
            var lu = gan + "命互禄"
            if (null != zhi) {
                lu += " " + zhi + "命进禄"
            }
            return lu
        }

    val time: LunarTime
        /**
         * 获取时辰
         *
         * @return 时辰
         */
        get() = LunarTime(year, month, day, hour, minute, second)

    val times: List<LunarTime>
        /**
         * 获取当天的时辰列表
         *
         * @return 时辰列表
         */
        get() {
            val l = ArrayList<LunarTime>()
            l.add(LunarTime(year, month, day, 0, 0, 0))
            for (i in 0..11) {
                l.add(LunarTime(year, month, day, (i + 1) * 2 - 1, 0, 0))
            }
            return l
        }

    val foto: Foto
        /**
         * 获取佛历
         *
         * @return 佛历
         */
        get() = Foto.fromLunar(this)

    val tao: Tao
        /**
         * 获取道历
         *
         * @return 佛历
         */
        get() = Tao.fromLunar(this)

    companion object {
        /**
         * 节气表，国标以冬至为首个气令
         */
        val JIE_QI: Array<String> = arrayOf<String>(
            "冬至",
            "小寒",
            "大寒",
            "立春",
            "雨水",
            "惊蛰",
            "春分",
            "清明",
            "谷雨",
            "立夏",
            "小满",
            "芒种",
            "夏至",
            "小暑",
            "大暑",
            "立秋",
            "处暑",
            "白露",
            "秋分",
            "寒露",
            "霜降",
            "立冬",
            "小雪",
            "大雪"
        )

        /**
         * 实际的节气表
         */
        val JIE_QI_IN_USE: Array<String> = arrayOf<String>(
            "DA_XUE",
            "冬至",
            "小寒",
            "大寒",
            "立春",
            "雨水",
            "惊蛰",
            "春分",
            "清明",
            "谷雨",
            "立夏",
            "小满",
            "芒种",
            "夏至",
            "小暑",
            "大暑",
            "立秋",
            "处暑",
            "白露",
            "秋分",
            "寒露",
            "霜降",
            "立冬",
            "小雪",
            "大雪",
            "DONG_ZHI",
            "XIAO_HAN",
            "DA_HAN",
            "LI_CHUN",
            "YU_SHUI",
            "JING_ZHE"
        )

        /**
         * 通过指定阳历日期获取农历
         *
         * @param date 阳历日期
         * @return 农历
         */
        fun fromDate(date: LocalDate): Lunar {
            return Lunar(date)
        }

        /**
         * 通过指定农历年月日获取农历
         *
         * @param lunarYear  年（农历）
         * @param lunarMonth 月（农历），1到12，闰月为负，即闰2月=-2
         * @param lunarDay   日（农历），1到31
         * @return 农历
         */
        fun fromYmd(lunarYear: Int, lunarMonth: Int, lunarDay: Int): Lunar {
            return Lunar(lunarYear, lunarMonth, lunarDay)
        }

        /**
         * 通过指定农历年月日获取农历
         *
         * @param lunarYear  年（农历）
         * @param lunarMonth 月（农历），1到12，闰月为负，即闰2月=-2
         * @param lunarDay   日（农历），1到31
         * @param hour       小时（阳历）
         * @param minute     分钟（阳历）
         * @param second     秒钟（阳历）
         * @return 农历
         */
        fun fromYmdHms(lunarYear: Int, lunarMonth: Int, lunarDay: Int, hour: Int, minute: Int, second: Int): Lunar {
            return Lunar(lunarYear, lunarMonth, lunarDay, hour, minute, second)
        }
    }
}

typealias LocalDateCache = Cache<LocalDate, Lunar>

fun LocalDate.toLunar(cache:LocalDateCache ?= null) = cache?.getOrPut(this){ Lunar(this) }?:Lunar(this)

fun LocalDateTime.toLunar(cache:LocalDateCache ?= null) = date.toLunar(cache)